/*
 * This file is part of the GUI_SDK.
 *
 * Copyright (c) 2019-2019 Link-Card.Co.Ltd. All rights reserved.
 *
 * Authors:
 *    JiangYong <1556631285@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * 'Software'), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Function:  key detect
 * Created on: 2019-10-05
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <assert.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <SDL2/SDL.h>

#include "bsp.h"
#include "key.h"
#include "gpio.h"
#include "event.h"
#include "log2file.h"

void gpio_init(void)
{
    gpio_t *gpio_key1, *gpio_key2, *gpio_key3;
    gpio_key1 = gpio_new();
    gpio_key2 = gpio_new();
    gpio_key3 = gpio_new();

    gpio_open_sysfs(gpio_key1, GPIO_KEY1, GPIO_DIR_IN);
    gpio_open_sysfs(gpio_key2, GPIO_KEY2, GPIO_DIR_IN);
    gpio_open_sysfs(gpio_key3, GPIO_KEY3, GPIO_DIR_IN);
    gpio_free(gpio_key1);
    gpio_free(gpio_key2);
    gpio_free(gpio_key3);
}

static void pushkey(eKeyVal key)
{
	SDL_Event event;
	SDL_zero(event);
	event.type = SDL_KEYUP;
	event.key.keysym.sym = key;
	SDL_PushEvent(&event);
}

static int shortkey_val(unsigned long val)
{
	int key;
	static int key1_times;
	switch (KEY_MASK & val)
	{
	case KEY_PRESS(KEY1):
		key = KEY1_SHORT;
		if (++key1_times == 5) {
			key = KEY1_5TIMES;
			key1_times = 0;
		}
		break;

	case KEY_PRESS(KEY2):
		key = KEY2_SHORT;
		key1_times = 0;
		break;

	case KEY_PRESS(KEY3):
		key = KEY3_SHORT;
		key1_times = 0;
		break;

	default:
		printf("shortkey_val error!\n");
		key = NO_KEY;
		break;
	}
	return key;
}

static int longkey_val(unsigned long val)
{
	int key;
	switch (KEY_MASK & val)
	{
	case KEY_PRESS(KEY1):
		key = KEY1_LONG;
		break;

	case KEY_PRESS(KEY2):
		key = KEY2_LONG;
		break;

	case KEY_PRESS(KEY3):
		key = KEY3_LONG;
		break;

	default:
		printf("longkey_val error!\n");
		key = NO_KEY;
		break;
	}
	return key;
}

static int Keypressed(unsigned long *pval)
{
	unsigned long val = 0;
	//read data in
	devmem_read(VIENNA_GPIOC_0_MMR_BASE + GPIOC_DATA_IN, &val);
	if((val & KEY_MASK) != KEY_MASK) {
		(*pval) = (val & KEY_MASK);
		return 1;
	} else {
		return 0;
	}
}

//函数原型：
//	long clock_gettime (clockid_t which_clock, struct timespec *tp);
//参数列表：
//	CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户该成其他,则对应的时间相应改变。
//	CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
//	CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间
//	CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间
//返回值：
//	0:成功，-1:错误，在errno中保存错误代码
//目的：测代码运行时间
static void diff(struct timespec *start, struct timespec *end, struct timespec *interv)
{
	if((end->tv_nsec - start->tv_nsec) < 0) {
		interv->tv_sec = end->tv_sec - start->tv_sec - 1;
		interv->tv_nsec = 1000000000 + end->tv_nsec - start->tv_nsec;
	} else {
		interv->tv_sec = end->tv_sec - start->tv_sec;
		interv->tv_nsec = end->tv_nsec - start->tv_nsec;
	}
}

static void ticks(void)
{
	static struct timespec gTick_start = {0};
	static int tick_300ms_cnt = 0;
	struct timespec end, interv;

	clock_gettime(CLOCK_MONOTONIC, &end);
	diff(&gTick_start, &end, &interv);
	if(interv.tv_nsec / 1000000 >= 100) {
		gTick_start = end;
		tick_300ms_cnt++;

		if(tick_300ms_cnt >= 3 * 10) {
			pushkey(KEY_TICK_300MS);
			tick_300ms_cnt = 0;
		}
	}
}

static void *Thread_Gpio2(void * p)
{
	int key_cnt = 0;
	int mKeyVal = 0;
	int long_key_press = 0;
	
	pthread_detach(pthread_self());

	while(onExit())
	{
		unsigned long val1 = 0xFFFF;
		unsigned long val2 = 0xFFFF;

		long_key_press = 0;
		ticks();

		while(Keypressed(&val1)) {
			if(val1 < val2) {
				val2 = val1;
				key_cnt = 0;
			}
			usleep(10 * 1000);
			key_cnt ++;

			if(150 == key_cnt) {
				mKeyVal = longkey_val(val2);
				pushkey(mKeyVal);
				long_key_press = 1;
			}
			
			ticks();
		}

		if((key_cnt > 3) && (0 == long_key_press)) {
			mKeyVal = shortkey_val(val2);
			pushkey(mKeyVal);
			key_cnt = 0;
		} else {
			usleep(100 * 1000);
		}
	}
	
	return NULL;
}

int create_keys_thread(void)
{
	pthread_t thread_key;
	if(-1 == pthread_create(&thread_key, NULL, Thread_Gpio2, NULL)) {
		perror("Create thread of Gpio");
		return -1;
	}
	return 0;
}